using System;
using System.IO;
using System.Runtime.InteropServices;

namespace dotGen.CustomTool.Fx
{
    public abstract class BaseCodeGenerator : IVsSingleFileGenerator
    {
        private IVsGeneratorProgress codeGeneratorProgress;
        private string codeFileNameSpace = string.Empty;
        private string codeFilePath = string.Empty;

        public abstract string GetDefaultExtension();

        public void Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace,
            out IntPtr rgbOutputFileContents, out int pcbOutput, IVsGeneratorProgress pGenerateProgress)
        {
            if (bstrInputFileContents == null)
                throw new ArgumentNullException(bstrInputFileContents);

            codeFilePath = wszInputFilePath;
            codeFileNameSpace = wszDefaultNamespace;
            codeGeneratorProgress = pGenerateProgress;

            byte[] bytes = GenerateCode(wszInputFilePath, bstrInputFileContents);

            if (bytes == null)
            {
                rgbOutputFileContents = IntPtr.Zero;
                pcbOutput = 0;
            }
            else
            {
                pcbOutput = bytes.Length;
                rgbOutputFileContents = Marshal.AllocCoTaskMem(pcbOutput);
                Marshal.Copy(bytes, 0, rgbOutputFileContents, pcbOutput);
            }
        }

        protected abstract byte[] GenerateCode(string inputFileName, string inputFileContent);

        protected virtual void GeneratorErrorCallback(bool warning, int level, string message, int line, int column)
        {
            IVsGeneratorProgress progress = CodeGeneratorProgress;
            if (progress != null)
                progress.GeneratorError(warning, level, message, line, column);
        }

        protected static byte[] StreamToBytes(Stream stream)
        {
            if (stream.Length == 0)
                return new byte[0];

            long position = stream.Position;
            stream.Position = 0;
            var bytes = new byte[(int)stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            stream.Position = position;

            return bytes;
        }

        protected string FileNameSpace
        {
            get { return codeFileNameSpace; }
        }

        protected string InputFilePath
        {
            get { return codeFilePath; }
        }

        internal IVsGeneratorProgress CodeGeneratorProgress
        {
            get { return codeGeneratorProgress; }
        }
    }
}